﻿using System;
using System.CodeDom;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading;
using System.Web.Services.Description;
using System.Web.Services.Protocols;
using System.Xml;
using System.Xml.Serialization;
using Microsoft.CSharp;
using NUnit.Framework;
using svcutil2;
using WsdlConverter;

namespace WsdlConverterTests
{
    [TestFixture]
    public class tests
    {
        private XmlDocument DownloadWsdl2Xml(string url)
        {
            var b = new WebClient().DownloadData(url);
            var wsdl2 = new XmlDocument();
            wsdl2.Load(new MemoryStream(b));
            return wsdl2;
        }

        private List<ServiceDescription> DownloadWsdl1Models(string url)
        {
            var b = new WebClient().DownloadData(url);
            var m = new MemoryStream(b);

            var res = new List<ServiceDescription>();
            var wsdl = ServiceDescription.Read(m);
            res.Add(wsdl);

            foreach (Import imp in wsdl.Imports)
            {
                var r = DownloadWsdl1Models(imp.Location);
                res.AddRange(r);
            }

            return res;
        }


        private Assembly CompileWsdl2_url(string wsdl2url)
        {
            var wsdl2 = DownloadWsdl2Xml(wsdl2url);
            var conv = new Wsdl2WsdlConverter();
            conv.DocumentReference += (object o, ref string s) => { };
            var wsdl1 = conv.Convert(wsdl2);
            return CompileWsdl1_model(new List<ServiceDescription>() { wsdl1 });
        }

        private Assembly CompileWsdl1_url(string wsdl1url)
        {
            var wsdl1 = DownloadWsdl1Models(wsdl1url);
            return CompileWsdl1_model(wsdl1);            
        }

        private Assembly CompileWsdl1_model(List<ServiceDescription> wsdl1)
        {
            var ns = new CodeNamespace("ns");
            var cu = new CodeCompileUnit();
            cu.Namespaces.Add(ns);


            var w = TryGenerateCode(cu, ns, wsdl1, "Soap");
            if (w==ServiceDescriptionImportWarnings.NoCodeGenerated)
                w = TryGenerateCode(cu, ns, wsdl1, "Soap12");


            if (false == cu.ReferencedAssemblies.Contains("System.dll"))
                cu.ReferencedAssemblies.Add("System.dll");
            if (false == cu.ReferencedAssemblies.Contains("System.Xml.dll"))
                cu.ReferencedAssemblies.Add("System.Xml.dll");
            if (false == cu.ReferencedAssemblies.Contains("System.Web.Services.dll"))
                cu.ReferencedAssemblies.Add("System.Web.Services.dll");
            if (false == cu.ReferencedAssemblies.Contains("System.Data.dll"))
                cu.ReferencedAssemblies.Add("System.Data.dll");

            CSharpCodeProvider cp = new CSharpCodeProvider();
            string asm = String.Format("test_{0}.dll", Guid.NewGuid());
            var cr = cp.CompileAssemblyFromDom(new System.CodeDom.Compiler.CompilerParameters() { OutputAssembly = asm }, cu);

            return cr.CompiledAssembly;
        }

        private ServiceDescriptionImportWarnings TryGenerateCode(CodeCompileUnit cu, CodeNamespace ns, List<ServiceDescription> wsdl1, string protocol)
        {
            var imp = new ServiceDescriptionImporter() { ProtocolName = protocol };

            foreach (var w in wsdl1)
            {
                imp.AddServiceDescription(w, null, null);
            }

            var warnings = imp.Import(ns, cu);
            return warnings;
        }


        [Test]
        public void import_sanity()
        {
            var asm = CompileWsdl2_url(@"Wsdls/Wsdl20/sanity12.wsdl");

            var methods = asm.GetType("ns.Service1").GetMethods();
            
            Assert.IsNotNull(methods.FirstOrDefault(p => p.Name == "EchoAddr"));
            Assert.IsNotNull(methods.FirstOrDefault(p => p.Name == "EchoAddrWithAttribute"));
        }
        
        [Test]
        public void import_sanity_soap12Only()
        {
            var asm = CompileWsdl2_url(@"Wsdls/Wsdl20/sanity12_soap12.wsdl");

            var methods = asm.GetType("ns.Service1").GetMethods();

            Assert.IsNotNull(methods.FirstOrDefault(p => p.Name == "EchoAddr"));
            Assert.IsNotNull(methods.FirstOrDefault(p => p.Name == "EchoAddrWithAttribute"));
        }

        [Test]
        public void import_wsdl_with_action_instead_of_soapAction()
        {
            var asm = CompileWsdl2_url(@"Wsdls/Wsdl20/wsdl_with_action_instead_of_soapAction.wsdl");

            var methods = asm.GetType("ns.FormattedStringTest_PHP").GetMethods();

            var m = methods.FirstOrDefault(p => p.Name == "getStatistic");
            Assert.IsNotNull(m);
            
            var attrs = m.GetCustomAttributes(typeof (SoapDocumentMethodAttribute), true);
            Assert.AreEqual("Function_001", ((SoapDocumentMethodAttribute)attrs[0]).Action);

        }

        [Test]
        public void import_header()
        {
            var asm = CompileWsdl2_url(@"Wsdls/Wsdl20/header12.wsdl");

            var methods = asm.GetType("ns.Service").GetMethods();

            var m = methods.FirstOrDefault(p => p.Name == "HelloWorld");

            var attr = m.GetCustomAttributes(typeof(SoapHeaderAttribute), true);
            Assert.IsNotNull(attr);

        }

        [Test]
        public void import_oneWay()
        {            
            throw new NotImplementedException("the unit test is not implemented");

            var asm = CompileWsdl2_url(@"Wsdls/Wsdl20/oneway12.wsdl");

            var methods = asm.GetType("ns.Service").GetMethods();

            var m = methods.FirstOrDefault(p => p.Name == "HelloWorld");

            Assert.IsNotNull(m);

            var attr = m.GetCustomAttributes(typeof(OneWayAttribute), true);
            Assert.IsNotNull(attr);
        }

        [Test]
        public void import_WithImports()
        {
            const string location = "bla";

            var wsdl2 = DownloadWsdl2Xml("Wsdls/Wsdl20/Imports/sanity_with_import.wsdl");
            var conv = new Wsdl2WsdlConverter();
            conv.DocumentReference += (object o, ref string s) =>
                                          {
                                              s = location;
                                          };
            var wsdl1 = conv.Convert(wsdl2);

            Assert.AreEqual(wsdl1.Imports[0].Location, location);
        }

        [Test]
        public void import_integration_sanity_disk()
        {
            const string prefix = @"http://localhost:8765/wsdl2wsdl/";

            var listener = new Wsdl2Listener(prefix);
            listener.Start();

            string url = "http://localhost:8765/wsdl2wsdl/?Wsdls/Wsdl20/sanity12.wsdl";


            var asm = CompileWsdl1_url(url);

            var methods = asm.GetType("ns.Service1").GetMethods();
            
            Assert.IsNotNull(methods.FirstOrDefault(p => p.Name == "EchoAddr"));
            Assert.IsNotNull(methods.FirstOrDefault(p => p.Name == "EchoAddrWithAttribute"));

            listener.Stop();
        }

        [Test]
        public void import_integration_simple_url()
        {

            const string prefix = @"http://localhost:8765/wsdl2wsdl/";

            var listener = new Wsdl2Listener(prefix);
            listener.Start();
            
            string url = "http://localhost:8765/wsdl2wsdl/?http://localhost/wsdl2sanity.wsdl";


            var asm = CompileWsdl1_url(url);

            var methods = asm.GetType("ns.Service1").GetMethods();

            Assert.IsNotNull(methods.FirstOrDefault(p => p.Name == "EchoAddr"));
            Assert.IsNotNull(methods.FirstOrDefault(p => p.Name == "EchoAddrWithAttribute"));

            listener.Stop();
        }

        [Test]
        public void import_integration_withImports()
        {
            const string prefix = @"http://localhost:8765/wsdl2wsdl/";

            var listener = new Wsdl2Listener(prefix);
            listener.Start();

            string url = "http://localhost:8765/wsdl2wsdl/?Wsdls/Wsdl20/Imports1/wsdl_root.wsdl";
            
            var asm = CompileWsdl1_url(url);

            var methods = asm.GetType("ns.Service1").GetMethods();

            Assert.IsNotNull(methods.FirstOrDefault(p => p.Name == "EchoAddr"));
            Assert.IsNotNull(methods.FirstOrDefault(p => p.Name == "EchoAddrWithAttribute"));

            listener.Stop();
        }
    }
}
